package com.wwu.system.service.impl;

import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.wwu.common.constant.ResultConst;
import com.wwu.common.utils.CronUtils;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.wwu.common.utils.AssertUtil;
import com.wwu.common.web.ResultObj;
import com.wwu.common.web.ResultPage;
import com.wwu.system.entity.SysQuartz;
import com.wwu.system.mapper.SysQuartzMapper;
import com.wwu.system.service.ISysQuartzLogService;
import com.wwu.system.service.ISysQuartzService;
import com.wwu.system.vo.QueryVO;
import lombok.extern.slf4j.Slf4j;
import org.quartz.*;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;

/**
 * 定时任务服务实现类
 * @author 一蓑烟雨
 * @version 1.0.0
 * @date 2023-08-13 18:09
 */
@Slf4j
@Service
public class SysQuartzServiceImpl  extends ServiceImpl<SysQuartzMapper, SysQuartz> implements ISysQuartzService {
    @Resource
    private Scheduler scheduler;
    @Resource
    private ISysQuartzLogService sysQuartzLogService;

    /**
     * 分页查询定时任务
     * @param pageNum  页码
     * @param pageSize  每页数量
     * @param sysQuartz 定时任务实体
     * @param queryVO   前端查询条件封装实体类
     * @return com.wwu.common.web.ResultPage<com.wwu.system.entity.SysQuartz>
     * @author 一蓑烟雨
     * @date 2023/8/13 17:54
     */
    @Override
    public ResultPage<SysQuartz> getQuartzListPage(Integer pageNum, Integer pageSize, SysQuartz sysQuartz, QueryVO queryVO) {
        Page<SysQuartz> page = new Page<>(pageNum,pageSize);
        LambdaQueryWrapper<SysQuartz> wrapper = new LambdaQueryWrapper<>();
        if(StringUtils.hasText(sysQuartz.getQuartzId())){
            wrapper.eq(SysQuartz::getQuartzId,sysQuartz.getQuartzId());
        }
        if(StringUtils.hasText(sysQuartz.getQuartzName())){
            wrapper.like(SysQuartz::getQuartzName,sysQuartz.getQuartzName());
        }
        if(!ObjectUtils.isEmpty(sysQuartz.getQuartzType())){
            wrapper.eq(SysQuartz::getQuartzType,sysQuartz.getQuartzType());
        }
        if(!ObjectUtils.isEmpty(sysQuartz.getQuartzStatus())){
            wrapper.eq(SysQuartz::getQuartzStatus,sysQuartz.getQuartzStatus());
        }
        if(StringUtils.hasText(queryVO.getStartDate()) && StringUtils.hasText(queryVO.getEndDate())){
            wrapper.between(SysQuartz::getLastExecTime,queryVO.getStartDate(),queryVO.getEndDate());
        }
        wrapper.orderByAsc(SysQuartz::getQuartzId);

        IPage<SysQuartz> sysQuartzPage = this.page(page, wrapper);
        return new ResultPage<>(sysQuartzPage);
    }

    /**
     * 根据任务名获取定时任务
     * @param quartzName   任务名
     * @return com.wwu.system.entity.SysQuartz
     * @author 一蓑烟雨
     * @date 2023/8/13 18:00
     */
    @Override
    public SysQuartz getQuartzByName(String quartzName) {
        AssertUtil.isTrue(!StringUtils.hasText(quartzName),"任务名不能为空");
        LambdaQueryWrapper<SysQuartz> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(SysQuartz::getQuartzName,quartzName);

        return this.getOne(wrapper);
    }

    /**
     * 根据任务执行类获取定时任务
     * @param quartzClass  任务执行类
     * @return com.wwu.system.entity.SysQuartz
     * @author 一蓑烟雨
     * @date 2023/8/16 14:38
     */
    @Override
    public SysQuartz getQuartzByClass(String quartzClass) {
        AssertUtil.isTrue(!StringUtils.hasText(quartzClass),"任务执行类不能为空");
        LambdaQueryWrapper<SysQuartz> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(SysQuartz::getQuartzClass,quartzClass);

        return this.getOne(wrapper);
    }

    /**
     * 添加定时任务
     * @param sysQuartz 定时任务实体
     * @author 一蓑烟雨
     * @date 2023/8/13 18:01
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
    public void saveQuartz(SysQuartz sysQuartz) {
        AssertUtil.isTrue(!StringUtils.hasText(sysQuartz.getQuartzName()),"任务名不能为空");
        AssertUtil.isTrue(!StringUtils.hasText(sysQuartz.getQuartzCron()),"cron表达式不能为空");
        AssertUtil.isTrue(!StringUtils.hasText(sysQuartz.getQuartzClass()),"任务执行类不能为空");
        SysQuartz sysQuartzTemp1 = this.getQuartzByName(sysQuartz.getQuartzName());
        AssertUtil.isTrue(null != sysQuartzTemp1 && !sysQuartz.getQuartzId().equals(sysQuartzTemp1.getQuartzId()),"任务名已存在");

        SysQuartz sysQuartzTemp2 = this.getQuartzByClass(sysQuartz.getQuartzClass());
        AssertUtil.isTrue(null != sysQuartzTemp2 && !sysQuartz.getQuartzId().equals(sysQuartzTemp2.getQuartzId()),"任务执行类已存在");

        sysQuartz.setQuartzDesc(StringUtils.hasText(sysQuartz.getQuartzDesc()) ? sysQuartz.getQuartzDesc() : sysQuartz.getQuartzName());
        sysQuartz.setQuartzCronZh(CronUtils.getCronCh(sysQuartz.getQuartzCron()));

        DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        LocalDateTime lastExecTime = LocalDateTime.parse(CronUtils.lastExecTime(sysQuartz.getQuartzCron()),df);
        LocalDateTime nextExecTime = LocalDateTime.parse(CronUtils.nextExecTime(sysQuartz.getQuartzCron()),df);
        sysQuartz.setLastExecTime(lastExecTime);
        sysQuartz.setNextExecTime(nextExecTime);

        //启动任务调度
        if(sysQuartz.getQuartzStatus().equals(0)){
            ResultObj resultObj = this.addQuartzScheduler(sysQuartz);
            AssertUtil.isTrue(resultObj.getCode()!= ResultConst.SUCCESS_CODE,"定时任务调度添加失败！");
        }

        AssertUtil.isTrue(!(this.save(sysQuartz)),"定时任务添加失败！");
    }

    /**
     * 更新定时任务
     * @param sysQuartz 定时任务实体
     * @return com.wwu.common.web.ResultObj
     * @author 一蓑烟雨
     * @date 2023/8/13 18:01
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
    public ResultObj updateQuartz(SysQuartz sysQuartz) {
        AssertUtil.isTrue(!StringUtils.hasText(sysQuartz.getQuartzName()),"任务名不能为空");
        AssertUtil.isTrue(!StringUtils.hasText(sysQuartz.getQuartzCron()),"cron表达式不能为空");
        AssertUtil.isTrue(!StringUtils.hasText(sysQuartz.getQuartzClass()),"任务执行类不能为空");
        SysQuartz sysQuartzTemp1 = this.getQuartzByName(sysQuartz.getQuartzName());
        AssertUtil.isTrue(null != sysQuartzTemp1 && !sysQuartz.getQuartzId().equals(sysQuartzTemp1.getQuartzId()),"任务名已存在");
        SysQuartz sysQuartzTemp2 = this.getQuartzByClass(sysQuartz.getQuartzClass());
        AssertUtil.isTrue(null != sysQuartzTemp2 && !sysQuartz.getQuartzId().equals(sysQuartzTemp2.getQuartzId()),"任务执行类已存在");


        sysQuartz.setQuartzDesc(StringUtils.hasText(sysQuartz.getQuartzDesc()) ? sysQuartz.getQuartzDesc() : sysQuartz.getQuartzName());
        sysQuartz.setQuartzCronZh(CronUtils.getCronCh(sysQuartz.getQuartzCron()));

        DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        LocalDateTime lastExecTime = LocalDateTime.parse(CronUtils.lastExecTime(sysQuartz.getQuartzCron()),df);
        LocalDateTime nextExecTime = LocalDateTime.parse(CronUtils.nextExecTime(sysQuartz.getQuartzCron()),df);
        sysQuartz.setLastExecTime(lastExecTime);
        sysQuartz.setNextExecTime(nextExecTime);

        //启动任务调度
        if(sysQuartz.getQuartzStatus().equals(0)){
            //删除任务调度
            ResultObj resultObj = this.deleteQuartzScheduler(sysQuartz);
            AssertUtil.isTrue(resultObj.getCode()!= ResultConst.SUCCESS_CODE,"定时任务调度删除失败！");
            //添加任务调度
            ResultObj resultObj1 = this.addQuartzScheduler(sysQuartz);
            AssertUtil.isTrue(resultObj1.getCode()!= ResultConst.SUCCESS_CODE,"定时任务调度添加失败！");
        }else{
            //停止任务调度
            this.stopQuartzScheduler(sysQuartz);
        }

        AssertUtil.isTrue(!(this.updateById(sysQuartz)),"定时任务更新失败！");
        return ResultObj.success();
    }

    /**
     * 更新定时任务启停状态
     * @param quartzId 任务id
     * @param quartzStatus 任务启动状态
     * @author 一蓑烟雨
     * @date 2023/8/13 18:02
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
    public void updateQuartzStatus(String quartzId, Integer quartzStatus) {
        AssertUtil.isTrue(!StringUtils.hasText(quartzId),"任务id不能为空");
        AssertUtil.isTrue(ObjectUtils.isEmpty(quartzStatus),"任务状态不能为空");

        SysQuartz sysQuartz = this.getById(quartzId);
        //启动任务调度
        if(quartzStatus.equals(0)){
            //删除任务调度
            ResultObj resultObj = this.deleteQuartzScheduler(sysQuartz);
            AssertUtil.isTrue(resultObj.getCode()!= ResultConst.SUCCESS_CODE,"定时任务调度删除失败！");
            //添加任务调度
            ResultObj resultObj1 = this.addQuartzScheduler(sysQuartz);
            AssertUtil.isTrue(resultObj1.getCode()!= ResultConst.SUCCESS_CODE,"定时任务调度添加失败！");
        }else{
            //停止任务调度
            this.stopQuartzScheduler(sysQuartz);
        }

        LambdaUpdateWrapper<SysQuartz> wrapper = new LambdaUpdateWrapper<>();
        wrapper.set(SysQuartz::getQuartzStatus,quartzStatus)
                .eq(SysQuartz::getQuartzId,quartzId);
        AssertUtil.isTrue(!this.update(wrapper),"定时任务状态更新失败！");
    }

    /**
     * 更新定时任务最近一次执行状态
     * @param quartzId 任务id
     * @param quartzExecStatus 任务执行状态
     * @author 一蓑烟雨
     * @date 2023/8/16 17:45
     */
    @Override
    public void updateQuartzExecStatus(String quartzId, Integer quartzExecStatus) {
        AssertUtil.isTrue(!StringUtils.hasText(quartzId),"任务id不能为空");
        AssertUtil.isTrue(ObjectUtils.isEmpty(quartzExecStatus),"任务状态不能为空");
        LambdaUpdateWrapper<SysQuartz> wrapper = new LambdaUpdateWrapper<>();
        wrapper.set(SysQuartz::getLastExecStatus,quartzExecStatus)
                .eq(SysQuartz::getQuartzId,quartzId);
        AssertUtil.isTrue(!this.update(wrapper),"定时任务执行状态更新失败！");
    }

    /**
     * 删除定时任务
     * @param quartzIds 任务id集合
     * @author 一蓑烟雨
     * @date 2023/8/13 18:03
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
    public void deleteQuartz(List<String> quartzIds) {
        AssertUtil.isTrue(ObjectUtils.isEmpty(quartzIds),"任务id不能为空");
        AssertUtil.isTrue(!this.removeByIds(quartzIds),"定时任务删除失败！");

        //删除任务日志
        sysQuartzLogService.deleteQuartzLogByIds(quartzIds);
    }

    /**
     * 删除任务调度
     * @param sysQuartz   定时任务实体类
     * @return com.wwu.common.web.ResultObj
     * @author 一蓑烟雨
     * @date 2023/8/16 12:01
     */
    @Override
    public ResultObj deleteQuartzScheduler(SysQuartz sysQuartz) {
        try {
            scheduler.pauseTrigger(TriggerKey.triggerKey(sysQuartz.getQuartzId()));
            scheduler.unscheduleJob(TriggerKey.triggerKey(sysQuartz.getQuartzId()));
            scheduler.deleteJob(JobKey.jobKey(sysQuartz.getQuartzId()));
        }catch (SchedulerException e) {
            return ResultObj.except("删除任务调度异常");
        }
        return  ResultObj.success("删除任务调度成功");
    }

    /**
     * 添加任务调度
     * @param sysQuartz   定时任务实体类
     * @return com.wwu.common.web.ResultObj
     * @author 一蓑烟雨
     * @date 2023/8/16 16:42
     */
    @Override
    public ResultObj addQuartzScheduler(SysQuartz sysQuartz) {
        try {
            //利用反射机制获取任务执行类
            Class<? extends Job> jobClass = (Class<? extends Job>)(Class.forName(sysQuartz.getQuartzClass()).newInstance().getClass());
            //设置任务明细，调用定义的任务逻辑
            JobDetail jobDetail = JobBuilder.newJob(jobClass)
                    .withIdentity(sysQuartz.getQuartzId())
                    .build();
            //设置任务触发器，cornTrigger规则定义执行规则
            CronTrigger cronTrigger = TriggerBuilder.newTrigger()
                    //通过键值对方式向job实现业务逻辑传参数
                    .usingJobData("quartzId",sysQuartz.getQuartzId())
                    .usingJobData("quartzName",sysQuartz.getQuartzName())
                    .usingJobData("quartzCron",sysQuartz.getQuartzCron())
                    .usingJobData("quartzClass",sysQuartz.getQuartzClass())
                    .usingJobData("quartzParam",sysQuartz.getQuartzParam())
                    .withIdentity(sysQuartz.getQuartzId())
                    //程序启动后间隔多久开始执行任务
                    .startAt(DateBuilder.futureDate(3, DateBuilder.IntervalUnit.SECOND))
                    //执行Cron表达式
                    .withSchedule(CronScheduleBuilder.cronSchedule(sysQuartz.getQuartzCron()))
                    .build();
            //把任务明细和触发器注册到任务调度中
            scheduler.scheduleJob(jobDetail,cronTrigger);
            //启动
            scheduler.start();
        }catch (Exception e) {
            return ResultObj.except("添加任务调度异常");
        }
        return  ResultObj.success("添加任务调度成功");
    }

    /**
     * 停止任务调度
     * @param sysQuartz   定时任务实体类
     * @return com.wwu.common.web.ResultObj
     * @author 一蓑烟雨
     * @date 2023/8/16 14:56
     */
    @Override
    public ResultObj stopQuartzScheduler(SysQuartz sysQuartz) {
        try {
            scheduler.pauseJob(JobKey.jobKey(sysQuartz.getQuartzId()));
        }catch (Exception e) {
            return ResultObj.except("停止任务调度异常");
        }
        return  ResultObj.success("停止任务调度成功");
    }
}
